package jumpd

import (
	"fmt"
	"io"
	"sync"
)

// type ConfigReadWriter interface {
// 	LoadConfig(string) bool
// 	Open() bool
// 	Size() (uint64, error)
// 	Read(uint64) (n int, err error)
// 	Write(buf []byte) (uint64, error)
// 	Close()
// }

// Single Download Multi Writer
type SDMW struct {
	Downloader ConfigReadWriter
	WriterList []ConfigReadWriter
	CacheSize  uint64
}

func (s *SDMW) SetDownloader(cr ConfigReadWriter) {
	s.Downloader = cr
}

func (s *SDMW) AddWriter(cr ConfigReadWriter) {
	s.WriterList = append(s.WriterList, cr)
}

func (s *SDMW) Init() bool {
	if len(s.WriterList) <= 0 {
		fmt.Println("no writer in list")
		return false
	}

	// 打开文件
	state := s.Downloader.LoadConfig()
	for i := 0; i < len(s.WriterList) && state; i++ {
		fmt.Printf("load config for idx: %v\n", i)
		if s.WriterList[i].LoadConfig() {
			state = s.WriterList[i].Open() && state
		} else {
			state = false
		}
	}

	// 对齐已下载数据的长度
	curSize, err := s.WriterList[0].Size()
	if err != nil {
		fmt.Printf("err: %v\n", err)
		return false
	}
	for i := 1; i < len(s.WriterList) && state; i++ {
		wSize, err := s.WriterList[i].Size()
		if err != nil || wSize != curSize {
			fmt.Printf("writer do not have same length: idx: %v, size: %v, err: %v\n", i, wSize, err)
			state = false
		}
		fmt.Printf("idx: %v, fileSize: %v\n", i, wSize)
	}

	fmt.Printf("Init Result: %v\n", state)
	return state
}

func (s *SDMW) Run() {
	fileSize, err := s.Downloader.Size()
	if err != nil {
		fmt.Printf("err: %v\n", err)
		return
	}
	fmt.Printf("origin file size: %v\n", fileSize)

	curOffset, err := s.WriterList[0].Size()
	if err != nil {
		fmt.Printf("err: %v\n", err)
		return
	}
	wCnt := len(s.WriterList)
	buffer := make([]byte, s.CacheSize*1024*1024) // MB

	for {
		n, err := s.Downloader.Read(buffer, curOffset)
		if err == io.EOF {
			fmt.Printf("read reach to EOF at offset: %v\n", curOffset)
			fmt.Printf("origin file size: %v\n", fileSize)
			break
		}
		if err != nil {
			fmt.Printf("err: %v\n", err)
			return
		}

		wg := &sync.WaitGroup{}
		wg.Add(wCnt)
		cnt_chan := make(chan int, wCnt)

		for i := 0; i < len(s.WriterList); i++ {
			go func(idx int) {
				defer wg.Done()
				write_n, err := s.WriterList[idx].WriteAt(buffer[:n], curOffset)
				if err != nil {
					fmt.Printf("writer %v happen error: %v\n", idx, err)
					cnt_chan <- -1
					return
				}
				if write_n != uint64(n) {
					fmt.Printf("real write: %v, need write: %v\n", write_n, n)
					cnt_chan <- -1
					return
				}
				cnt_chan <- 1
			}(i)
		}

		wg.Wait()

		count := 0
		ok := true
		for stat := range cnt_chan {
			if stat == 1 {
				count++
			} else {
				ok = false
			}
		}

		if count == wCnt && ok {
			curOffset += uint64(n)
		} else {
			fmt.Printf("write is not sync in offset: %v\n", curOffset)
		}
	}

	fmt.Printf("Download Task is Completed\n")
	curOffset, err = s.WriterList[0].Size()
	if err != nil {
		fmt.Printf("err: %v\n", err)
		return
	}
	fmt.Printf("Finally check offset: %v\n", curOffset)
}

func (s *SDMW) Close() {
	s.Downloader.Close()
	for i := 1; i < len(s.WriterList); i++ {
		s.WriterList[i].Close()
	}
}
